package com.witmore.ftp.web.listener;

import com.witmore.ftp.db.entity.FtpConnectionDO;
import com.witmore.ftp.db.entity.FtpFileLogDO;
import com.witmore.ftp.db.mapper.FtpConnectionMapper;
import com.witmore.ftp.db.mapper.FtpFileLogMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ftpserver.ftplet.*;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.ConcurrentHashMap;

/**
 * FTP服务器事件监听器
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class FtpServerListener implements Ftplet {

    private final FtpFileLogMapper ftpFileLogMapper;
    private final FtpConnectionMapper ftpConnectionMapper;

    // 存储连接信息，用于计算连接时长
    private final ConcurrentHashMap<String, ConnectionInfo> connectionMap = new ConcurrentHashMap<>();

    @Override
    public FtpletResult beforeCommand(FtpSession session, FtpRequest request) throws FtpException, IOException {
        String command = request.getCommand().toUpperCase();
        String username = session.getUser() != null ? session.getUser().getName() : "anonymous";
        String clientIp = session.getClientAddress().getAddress().getHostAddress();

        log.debug("Before command - User: {}, IP: {}, Command: {}", username, clientIp, command);

        return FtpletResult.DEFAULT;
    }

    @Override
    public FtpletResult afterCommand(FtpSession session, FtpRequest request, FtpReply reply) throws FtpException, IOException {
        String command = request.getCommand().toUpperCase();
        String username = session.getUser() != null ? session.getUser().getName() : "anonymous";
        String clientIp = session.getClientAddress().getAddress().getHostAddress();
        String argument = request.getArgument();

        log.debug("After command - User: {}, IP: {}, Command: {}, Reply: {}",
                 username, clientIp, command, reply.getCode());

        // 记录文件操作日志
        if (isFileOperation(command)) {
            recordFileOperation(session, command, argument, reply);
        }

        return FtpletResult.DEFAULT;
    }

    @Override
    public FtpletResult onConnect(FtpSession session) throws FtpException, IOException {
        String sessionId = session.getSessionId().toString();
        String clientIp = session.getClientAddress().getAddress().getHostAddress();
        int clientPort = session.getClientAddress().getPort();

        log.info("Client connected - SessionId: {}, IP: {}, Port: {}", sessionId, clientIp, clientPort);

        // 记录连接信息
        ConnectionInfo connectionInfo = new ConnectionInfo();
        connectionInfo.setSessionId(sessionId);
        connectionInfo.setClientIp(clientIp);
        connectionInfo.setClientPort(clientPort);
        connectionInfo.setConnectTime(LocalDateTime.now());
        connectionMap.put(sessionId, connectionInfo);

        // 保存到数据库
        try {
            FtpConnectionDO ftpConnection = new FtpConnectionDO();
            ftpConnection.setSessionId(sessionId);
            ftpConnection.setClientIp(clientIp);
            ftpConnection.setClientPort(clientPort);
            ftpConnection.setConnectTime(LocalDateTime.now());
            ftpConnection.setStatus("CONNECTED");
            ftpConnection.setCreator(1); // 系统创建
            ftpConnection.setUpdator(1); // 系统创建
            ftpConnection.setCreateTime(LocalDateTime.now());
            ftpConnection.setUpdateTime(LocalDateTime.now());
            ftpConnection.setIsDeleted(0);
            ftpConnectionMapper.insert(ftpConnection);
        } catch (Exception e) {
            log.error("Failed to save connection record", e);
        }

        return FtpletResult.DEFAULT;
    }

    @Override
    public void init(FtpletContext ftpletContext) throws FtpException {
        log.info("FTP Server Listener initialized");
    }

    @Override
    public void destroy() {
        log.info("FTP Server Listener destroyed");
    }

    @Override
    public FtpletResult onDisconnect(FtpSession session) throws FtpException, IOException {
        String sessionId = session.getSessionId().toString();
        String username = session.getUser() != null ? session.getUser().getName() : "anonymous";
        String clientIp = session.getClientAddress().getAddress().getHostAddress();

        log.info("Client disconnected - SessionId: {}, User: {}, IP: {}", sessionId, username, clientIp);

        // 计算连接时长并更新数据库
        ConnectionInfo connectionInfo = connectionMap.remove(sessionId);
        if (connectionInfo != null) {
            LocalDateTime disconnectTime = LocalDateTime.now();
            long duration = ChronoUnit.SECONDS.between(connectionInfo.getConnectTime(), disconnectTime);

            try {
                // 更新连接记录
                FtpConnectionDO ftpConnection = new FtpConnectionDO();
                ftpConnection.setSessionId(sessionId);
                ftpConnection.setUsername(username);
                ftpConnection.setDisconnectTime(disconnectTime);
                ftpConnection.setStatus("DISCONNECTED");
                ftpConnection.setDuration(duration);
                ftpConnection.setCreator(1); // 系统创建
                ftpConnection.setUpdator(1); // 系统更新
                ftpConnection.setCreateTime(LocalDateTime.now());
                ftpConnection.setUpdateTime(LocalDateTime.now());
                ftpConnection.setIsDeleted(0);
                // 这里应该用update方法，但为了简化，我们先用这种方式
                ftpConnectionMapper.insert(ftpConnection);
            } catch (Exception e) {
                log.error("Failed to update connection record", e);
            }
        }

        return FtpletResult.DEFAULT;
    }

    /**
     * 判断是否为文件操作命令
     */
    private boolean isFileOperation(String command) {
        return "STOR".equals(command) || "RETR".equals(command) ||
               "DELE".equals(command) || "RNFR".equals(command) || "RNTO".equals(command);
    }

    /**
     * 记录文件操作日志
     */
    private void recordFileOperation(FtpSession session, String command, String argument, FtpReply reply) {
        try {
            String username = session.getUser() != null ? session.getUser().getName() : "anonymous";
            String clientIp = session.getClientAddress().getAddress().getHostAddress();

            FtpFileLogDO fileLog = new FtpFileLogDO();
            fileLog.setUsername(username);
            fileLog.setClientIp(clientIp);
            fileLog.setOperationType(getOperationType(command));
            fileLog.setFilePath(argument);
            fileLog.setFileName(extractFileName(argument));
            fileLog.setStatus(reply.getCode() >= 200 && reply.getCode() < 300 ? "SUCCESS" : "FAILED");

            if (reply.getCode() >= 400) {
                fileLog.setErrorMessage(reply.getMessage());
            }
            fileLog.setCreator(1); // 系统创建
            fileLog.setUpdator(1); // 系统创建
            fileLog.setCreateTime(LocalDateTime.now());
            fileLog.setUpdateTime(LocalDateTime.now());
            fileLog.setIsDeleted(0);
            ftpFileLogMapper.insert(fileLog);

            log.info("File operation logged - User: {}, Operation: {}, File: {}, Status: {}",
                    username, fileLog.getOperationType(), argument, fileLog.getStatus());
        } catch (Exception e) {
            log.error("Failed to record file operation log", e);
        }
    }

    /**
     * 获取操作类型
     */
    private String getOperationType(String command) {
        switch (command) {
            case "STOR":
                return "UPLOAD";
            case "RETR":
                return "DOWNLOAD";
            case "DELE":
                return "DELETE";
            case "RNFR":
            case "RNTO":
                return "RENAME";
            default:
                return command;
        }
    }

    /**
     * 从路径中提取文件名
     */
    private String extractFileName(String filePath) {
        if (filePath == null || filePath.isEmpty()) {
            return "";
        }

        int lastSlash = filePath.lastIndexOf('/');
        if (lastSlash >= 0 && lastSlash < filePath.length() - 1) {
            return filePath.substring(lastSlash + 1);
        }

        int lastBackslash = filePath.lastIndexOf('\\');
        if (lastBackslash >= 0 && lastBackslash < filePath.length() - 1) {
            return filePath.substring(lastBackslash + 1);
        }

        return filePath;
    }

    /**
     * 连接信息内部类
     */
    private static class ConnectionInfo {
        private String sessionId;
        private String clientIp;
        private int clientPort;
        private LocalDateTime connectTime;

        // Getters and Setters
        public String getSessionId() { return sessionId; }
        public void setSessionId(String sessionId) { this.sessionId = sessionId; }
        public String getClientIp() { return clientIp; }
        public void setClientIp(String clientIp) { this.clientIp = clientIp; }
        public int getClientPort() { return clientPort; }
        public void setClientPort(int clientPort) { this.clientPort = clientPort; }
        public LocalDateTime getConnectTime() { return connectTime; }
        public void setConnectTime(LocalDateTime connectTime) { this.connectTime = connectTime; }
    }
}
